package com.mtauto.service;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

import com.mtauto.exception.MtautoException;
import com.mtauto.exception.MtautoException.MtautoCode;

/**
 * monkeyrunner服务 TODO 目前存在阻塞的风险，后面需要根据上下文内容增加超时机制
 * 
 * @author ke.chenk 2013-11-20 下午5:16:15
 */
public class MonkeyRunnerService {

    private static int                   BUF_BASE_SIZE               = 1024;
    private static int                   QUEUE_MAX_SIZE              = 100;
    private static int                   MAX_MONKEY_RUNNER_INSTANCES = 20;
    @SuppressWarnings("unchecked")
    private static BlockingQueue<String> queue[]                     = new ArrayBlockingQueue[MAX_MONKEY_RUNNER_INSTANCES];
    private String                       monkeyRunner;
    private int                          mRunnerInstanceNumber       = 0;
    private Object                       instanceLock                = new Object();

    public void setMonkeyRunner(String monkeyRunner) {
        this.monkeyRunner = monkeyRunner;
    }

    public MonkeyRunnerProcess createMonkeyRunnerInstance(String deviceId, Float timeout) throws IOException,
                                                                                         InterruptedException {
        Process process = null;
        int threadId;
        synchronized (instanceLock) {
            if (mRunnerInstanceNumber++ > MAX_MONKEY_RUNNER_INSTANCES) {
                throw new MtautoException(MtautoCode.OVER_MAX_INSTANCE);
            }
            process = Runtime.getRuntime().exec(monkeyRunner);
            threadId = mRunnerInstanceNumber - 1;
            if (queue[threadId] == null) {
                queue[threadId] = new ArrayBlockingQueue<String>(QUEUE_MAX_SIZE);
            }
            startReadThread(process, threadId);
            System.out.println(queue[threadId].take());
            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
            writer.write("from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice\r");
            writer.write("device = MonkeyRunner.waitForConnection(" + timeout + ",\"" + deviceId + "\")\r");
            writer.write("print device\r");
            writer.flush();
            System.out.println(queue[threadId].take());
            System.out.println(queue[threadId].take());
            System.out.println(queue[threadId].take());
        }
        return new MonkeyRunnerProcess(process, threadId, deviceId);
    }

    public void flashSnapshot(MonkeyRunnerProcess monkeyRunnerProcess, String savePath) throws IOException,
                                                                                       InterruptedException {
        BufferedWriter writer = new BufferedWriter(
                                                   new OutputStreamWriter(
                                                                          monkeyRunnerProcess.getProcess().getOutputStream()));
        synchronized (monkeyRunnerProcess) {
            writer.write("result = device.takeSnapshot()\r");
            writer.flush();
            System.out.println(queue[monkeyRunnerProcess.getThreadId()].take());
            writer.write("result.writeToFile('" + savePath + "','png')\r");
            writer.flush();
            System.out.println(queue[monkeyRunnerProcess.getThreadId()].take());
        }

    }

    public void executeCmd(MonkeyRunnerProcess monkeyRunnerProcess, MonkeyRunnerCmd cmd) {
        BufferedWriter writer = new BufferedWriter(
                                                   new OutputStreamWriter(
                                                                          monkeyRunnerProcess.getProcess().getOutputStream()));
        executeCmd(writer, monkeyRunnerProcess, cmd);
    }

    private void executeCmd(BufferedWriter writer, MonkeyRunnerProcess monkeyRunnerProcess, MonkeyRunnerCmd cmd) {
        if (cmd == MonkeyRunnerCmd.touchCmd) {
            processTouchCmd(writer, monkeyRunnerProcess, cmd);
        }
    }

    private void processTouchCmd(BufferedWriter writer, MonkeyRunnerProcess monkeyRunnerProcess, MonkeyRunnerCmd cmd) {
        try {
            writer.write("device.touch (integer x, integer y, integer type)\r");
            writer.flush();
            System.out.println(queue[monkeyRunnerProcess.getThreadId()].take());
        } catch (Exception e) {
            throw new MtautoException(MtautoCode.IO_EXCEPTION, e);
        }
    }

    private void startReadThread(final Process p, final int threadId) {
        Thread t = new Thread(new Runnable() {

            public void run() {
                InputStream reader = p.getInputStream();
                int greaterCount = 0;
                byte[] buf = new byte[BUF_BASE_SIZE];
                int bufIndex = 0;
                try {
                    while (true) {
                        if (greaterCount == 3) {
                            queue[threadId].offer(new String(buf, 0, bufIndex));
                            buf = new byte[BUF_BASE_SIZE];
                            bufIndex = 0;
                            greaterCount = 0;
                        }
                        int v = reader.read();
                        char c = (char) v;
                        if (c == '>' && bufIndex == 0)

                        {
                            greaterCount++;
                        } else if (c == '>' && (char) buf[bufIndex - 1] != '>') {
                            greaterCount++;
                        } else if (c == '>' && (char) buf[bufIndex - 1] == '>') {
                            greaterCount++;
                        } else if (c != '>' && bufIndex > 0 && (char) buf[bufIndex - 1] == '>') {
                            greaterCount = 0;
                            bufIndex--;
                        }

                        if (bufIndex + 1 == buf.length) {
                            byte[] tmp = new byte[buf.length * 2];
                            System.arraycopy(buf, 0, tmp, 0, buf.length);
                            buf = tmp;
                        }
                        buf[bufIndex++] = (byte) v;
                    }
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        });
        t.setName("monkey-runner-instance-" + threadId);
        t.setDaemon(true);
        t.start();
    }

    public static void main2(String[] args) throws Exception {
        String cmd = "D:\\android\\adt-bundle-windows-x86\\sdk\\tools\\monkeyrunner.bat";
        Process proc = Runtime.getRuntime().exec(cmd);
        int len;
        byte[] buffer = new byte[1024 * 100];
        BufferedInputStream errin = new BufferedInputStream(proc.getErrorStream());
        BufferedInputStream stdin = new BufferedInputStream(proc.getInputStream());
        len = errin.read(buffer);
        System.out.println(new String(buffer, 0, len));
        len = stdin.read(buffer);
        System.out.println(new String(buffer, 0, len));
        BufferedOutputStream stdout = new BufferedOutputStream(proc.getOutputStream());
        stdout.write("print 'Runtime#getRuntime' \r".getBytes());
        stdout.flush();
        Thread.sleep(1000);
        len = stdin.read(buffer);
        System.out.println(new String(buffer, 0, len));
        proc.destroy();
    }

    public static void main1(String args[]) throws Exception {
        queue[0].offer("test1");
        queue[0].offer("test2");
        System.out.println(queue[0].take());
        System.out.println(queue[0].take());
    }

    private static void makeThread() {
        Thread t = new Thread() {

            public void run() {
                while (true) {
                    System.out.println("1");
                }
            }
        };
        t.setDaemon(true);
        t.start();
    }

    public static void main(String args[]) throws Exception {
        // makeThread();
        // while (true)
        // ;
        Timer timer = new Timer();
        TimerTask t1 = new TimerTask() {

            @Override
            public void run() {
                System.out.println("2");

            }
        };
        TimerTask t2 = new TimerTask() {

            @Override
            public void run() {
                System.out.println("3");

            }
        };
        timer.schedule(t1, 1000, 1000);
        Thread.sleep(3000);
        t1.cancel();
        timer.purge();
        timer.schedule(t1, 1000, 1000);
        Thread.sleep(3000);
        t1.cancel();
    }

    public static void main3(String args[]) throws Exception {

        final Process p = Runtime.getRuntime().exec("/home/chenke/softs/adt-bundle-linux-x86-20131030/sdk/tools/monkeyrunner");
        new Thread(new Runnable() {

            public void run() {
                InputStream reader = p.getInputStream();
                int greaterCount = 0;
                byte[] buf = new byte[BUF_BASE_SIZE];
                int bufIndex = 0;
                try {
                    while (true) {
                        if (greaterCount == 3) {
                            // TODO Sysout buf
                            System.out.println("print start:");
                            System.out.println(new String(buf, 0, bufIndex));
                            System.out.println("print end:");
                            buf = new byte[BUF_BASE_SIZE];
                            bufIndex = 0;
                            greaterCount = 0;
                        }
                        int v = reader.read();
                        char c = (char) v;
                        if (c == '>' && bufIndex == 0) {
                            greaterCount++;
                        } else if (c == '>' && (char) buf[bufIndex - 1] != '>') {
                            greaterCount++;
                        } else if (c == '>' && (char) buf[bufIndex - 1] == '>') {
                            greaterCount++;
                        } else if (c != '>' && bufIndex > 0 && (char) buf[bufIndex - 1] == '>') {
                            greaterCount = 0;
                            bufIndex--;
                        }

                        if (bufIndex + 1 == buf.length) {
                            byte[] tmp = new byte[buf.length * 2];
                            System.arraycopy(buf, 0, tmp, 0, buf.length);
                            buf = tmp;
                        }
                        buf[bufIndex++] = (byte) v;
                    }
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }).start();

        new Thread(new Runnable() {

            public void run() {
                try {
                    while (true) {
                        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(p.getOutputStream()));
                        // writer.write("print 'bbbbb'\r");
                        writer.write("from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice\r");
                        writer.write("device = MonkeyRunner.waitForConnection()\r");
                        // writer.write("print device\r");
                        writer.flush();
                        System.out.println("write code to console");
                        Thread.sleep(1000);

                    }
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }).start();
        InputStream reader = p.getErrorStream();
        byte[] buf = new byte[1024];
        int len;
        while ((len = reader.read(buf)) != -1) {
            System.out.println(new String(buf, 0, len));
        }
        // BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(p.getOutputStream()));
        // writer.write("from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice\r\n");
        // writer.write("device = MonkeyRunner.waitForConnection()\r\n");
        // writer.write("print device\r\n");
        //
        // while ((line = reader.readLine()) != null) {
        // System.out.println(line);
        // }
        /**
         * result = device.takeSnapshot() # Writes the screenshot to a file
         * result.writeToFile('myproject/shot1.png','png')
         */

    }

    public static class MonkeyRunnerProcess {

        private Process process;
        private int     threadId;
        private String  deviceId;
        private Timer   timer = new Timer();

        public MonkeyRunnerProcess(Process process, int threadId, String deviceId){
            this.process = process;
            this.threadId = threadId;
            this.deviceId = deviceId;
        }

        public Process getProcess() {
            return process;
        }

        public void setProcess(Process process) {
            this.process = process;
        }

        public int getThreadId() {
            return threadId;
        }

        public void setThreadId(int threadId) {
            this.threadId = threadId;
        }

        public String getDeviceId() {
            return deviceId;
        }

        public Timer getTimer() {
            return timer;
        }

    }

    public static class MonkeyRunnerCmd {

        public static enum MonkeyRunnerCmdEnum {
            touch, drag, type, press
        }

        private String                cmd;
        public static MonkeyRunnerCmd touchCmd = new MonkeyRunnerCmd("touch");
        public static MonkeyRunnerCmd dragCmd  = new MonkeyRunnerCmd("drag");
        public static MonkeyRunnerCmd typeCmd  = new MonkeyRunnerCmd("type");
        public static MonkeyRunnerCmd pressCmd = new MonkeyRunnerCmd("press");
        private Map<Object, Object>   params   = new HashMap<Object, Object>();

        public MonkeyRunnerCmd(String cmd){
            this.cmd = cmd;
        }

        public String getCmd() {
            return cmd;
        }

        public void setCmd(String cmd) {
            this.cmd = cmd;
        }

        public void addParam(Object key, Object val) {
            params.put(key, val);
        }

        public Map<Object, Object> getParams() {
            return params;
        }

        public MonkeyRunnerCmd createMonkeyRunnerCmd(MonkeyRunnerCmdEnum cmd) {
            if (cmd == MonkeyRunnerCmdEnum.touch) {
                return touchCmd;
            }
            if (cmd == MonkeyRunnerCmdEnum.drag) {
                return dragCmd;
            }

            if (cmd == MonkeyRunnerCmdEnum.type) {
                return typeCmd;
            }

            if (cmd == MonkeyRunnerCmdEnum.press) {
                return pressCmd;
            }

            throw new MtautoException(MtautoCode.UNKNOW_EXCEPTION);
        }
    }
}
